import sharp from 'sharp'
import log from 'electron-log'
import pixelmatch from 'pixelmatch'

/**
 * 图像匹配服务
 * 实现简单但高效的模板匹配算法
 */

interface MatchResult {
  found: boolean
  confidence: number
  x?: number
  y?: number
  templateName?: string
}

interface Template {
  id: string
  name: string
  path: string
  threshold: number
  imageData?: {
    data: Buffer
    width: number
    height: number
  }
}

export class ImageMatchService {
  private templates: Map<string, Template> = new Map()

  /**
   * 加载模板图片
   */
  async loadTemplate(template: { id: string; name: string; path: string; threshold: number }): Promise<void> {
    try {
      const startTime = Date.now()
      log.info(`[图像匹配] 开始加载模板: ${template.name}`)
      
      // 使用 sharp 读取图片并转为原始像素数据
      const image = sharp(template.path)
      
      const t1 = Date.now()
      const metadata = await image.metadata()
      log.info(`[图像匹配] - 读取元数据耗时: ${Date.now() - t1}ms, 尺寸: ${metadata.width}x${metadata.height}`)
      
      const t2 = Date.now()
      const rawData = await image
        .ensureAlpha() // 确保有alpha通道
        .raw()
        .toBuffer()
      log.info(`[图像匹配] - 转换原始数据耗时: ${Date.now() - t2}ms, 大小: ${(rawData.length / 1024 / 1024).toFixed(2)}MB`)

      this.templates.set(template.id, {
        ...template,
        imageData: {
          data: rawData,
          width: metadata.width!,
          height: metadata.height!,
        },
      })
      
      const totalTime = Date.now() - startTime
      log.info(`[图像匹配] ✅ 模板加载完成: ${template.name}, 总耗时: ${totalTime}ms`)
    } catch (error) {
      log.error(`[图像匹配] 加载模板失败: ${template.name}`, error)
      throw error
    }
  }

  /**
   * 卸载所有模板
   */
  clearTemplates(): void {
    this.templates.clear()
    log.info('[图像匹配] 已清空所有模板')
  }

  /**
   * 在屏幕截图中查找模板
   */
  async findTemplates(screenshotBuffer: Buffer): Promise<MatchResult[]> {
    if (this.templates.size === 0) {
      return []
    }

    try {
      // 处理屏幕截图
      const screenshot = sharp(screenshotBuffer)
      const screenshotMetadata = await screenshot.metadata()
      const screenshotData = await screenshot
        .ensureAlpha()
        .raw()
        .toBuffer()

      const results: MatchResult[] = []

      // 对每个模板进行匹配
      for (const [id, template] of this.templates) {
        if (!template.imageData) {
          continue
        }

        const startTime = Date.now()
        const matchResult = await this.matchTemplate(
          screenshotData,
          screenshotMetadata.width!,
          screenshotMetadata.height!,
          template.imageData.data,
          template.imageData.width,
          template.imageData.height,
          template.threshold
        )

        const elapsed = Date.now() - startTime
        const confidencePercent = (matchResult.confidence * 100).toFixed(2)
        const thresholdPercent = (template.threshold * 100).toFixed(0)
        
        if (matchResult.found) {
          log.info(`[图像匹配] ✅ 找到匹配: ${template.name}`)
          log.info(`   └─ 置信度: ${confidencePercent}% (阈值: ${thresholdPercent}%), 耗时: ${elapsed}ms`)
          results.push({
            ...matchResult,
            templateName: template.name,
          })
        } else {
          log.info(`[图像匹配] ❌ 未匹配: ${template.name}`)
          log.info(`   └─ 最高置信度: ${confidencePercent}% < 阈值: ${thresholdPercent}%, 耗时: ${elapsed}ms`)
          
          // 💡 如果置信度接近阈值（差距<5%），给出提示
          if (matchResult.confidence > template.threshold - 0.05) {
            log.info(`   └─ 💡 提示: 置信度很接近阈值，建议将阈值降低到 ${(matchResult.confidence * 100 - 2).toFixed(0)}%`)
          }
        }
      }

      return results
    } catch (error) {
      log.error('[图像匹配] 匹配过程出错:', error)
      return []
    }
  }

  /**
   * 模板匹配算法 - 多尺度匹配（适应缩放）
   * 在多个缩放级别下进行匹配，解决目标图片可能放大/缩小的问题
   */
  private async matchTemplate(
    screenData: Buffer,
    screenWidth: number,
    screenHeight: number,
    templateData: Buffer,
    templateWidth: number,
    templateHeight: number,
    threshold: number
  ): Promise<MatchResult> {
    if (templateWidth > screenWidth || templateHeight > screenHeight) {
      return { found: false, confidence: 0 }
    }

    let globalMaxConfidence = 0
    let globalBestX = 0
    let globalBestY = 0
    let globalBestScale = 1.0

    // 🎯 多尺度匹配：在不同缩放级别下查找
    // 缩放范围：80% ~ 120%，步进 10%
    const scales = [0.8, 0.9, 1.0, 1.1, 1.2]
    
    log.info(`[图像匹配] 开始多尺度匹配，缩放级别: ${scales.join(', ')}`)

    for (const scale of scales) {
      const scaledWidth = Math.round(templateWidth * scale)
      const scaledHeight = Math.round(templateHeight * scale)
      
      // 跳过超出屏幕的缩放级别
      if (scaledWidth > screenWidth || scaledHeight > screenHeight) {
        log.info(`[图像匹配] 跳过缩放 ${(scale * 100).toFixed(0)}% - 超出屏幕范围`)
        continue
      }

      log.info(`[图像匹配] 🔍 尝试缩放级别: ${(scale * 100).toFixed(0)}% (${scaledWidth}x${scaledHeight})`)
      
      // 缩放模板图片
      let scaledTemplateData = templateData
      if (scale !== 1.0) {
        try {
          const scaledBuffer = await sharp(templateData, {
            raw: {
              width: templateWidth,
              height: templateHeight,
              channels: 4
            }
          })
            .resize(scaledWidth, scaledHeight)
            .ensureAlpha()
            .raw()
            .toBuffer()
          
          scaledTemplateData = scaledBuffer
        } catch (error) {
          log.error(`[图像匹配] 缩放模板失败 at ${scale}:`, error)
          continue
        }
      }

      // 在当前缩放级别下进行匹配
      const result = await this.matchTemplateAtScale(
        screenData,
        screenWidth,
        screenHeight,
        scaledTemplateData,
        scaledWidth,
        scaledHeight,
        scale
      )

      if (result.confidence > globalMaxConfidence) {
        globalMaxConfidence = result.confidence
        globalBestX = result.x || 0
        globalBestY = result.y || 0
        globalBestScale = scale
        log.info(`[图像匹配] 🎯 发现更高匹配 at 缩放${(scale * 100).toFixed(0)}%: ${(result.confidence * 100).toFixed(2)}%`)
      }
    }

    log.info(`[图像匹配] 多尺度匹配完成: 最佳缩放=${(globalBestScale * 100).toFixed(0)}%, 置信度=${(globalMaxConfidence * 100).toFixed(2)}%`)

    return {
      found: globalMaxConfidence >= threshold,
      confidence: globalMaxConfidence,
      x: globalBestX,
      y: globalBestY,
    }
  }

  /**
   * 在指定缩放级别下进行模板匹配
   */
  private async matchTemplateAtScale(
    screenData: Buffer,
    screenWidth: number,
    screenHeight: number,
    templateData: Buffer,
    templateWidth: number,
    templateHeight: number,
    scale: number
  ): Promise<MatchResult> {
    let maxConfidence = 0
    let bestX = 0
    let bestY = 0

    // 🎯 优化步长：减小步长提高匹配准确度
    // 步长为模板较小边的 1/12，更精确但速度稍慢
    const stepSize = Math.max(5, Math.floor(Math.min(templateWidth, templateHeight) / 12))
    
    log.info(`[图像匹配] 扫描参数 - 屏幕: ${screenWidth}x${screenHeight}, 模板: ${templateWidth}x${templateHeight}, 步长: ${stepSize}`)

    let scanCount = 0
    let totalScans = Math.ceil((screenHeight - templateHeight) / stepSize) * Math.ceil((screenWidth - templateWidth) / stepSize)
    
    // 滑动窗口扫描
    for (let y = 0; y <= screenHeight - templateHeight; y += stepSize) {
      for (let x = 0; x <= screenWidth - templateWidth; x += stepSize) {
        scanCount++
        
        const confidence = this.calculateSimilarity(
          screenData,
          screenWidth,
          screenHeight,
          x,
          y,
          templateData,
          templateWidth,
          templateHeight,
          false  // 粗扫描
        )

        if (confidence > maxConfidence) {
          maxConfidence = confidence
          bestX = x
          bestY = y
          log.info(`[图像匹配] 🔍 发现更高相似度 at (${x},${y}): ${(confidence * 100).toFixed(2)}%`)
        }
        
        // 每扫描25%打印进度
        if (scanCount % Math.floor(totalScans / 4) === 0) {
          log.info(`[图像匹配] 扫描进度: ${scanCount}/${totalScans} (${(scanCount/totalScans*100).toFixed(0)}%), 当前最高: ${(maxConfidence * 100).toFixed(2)}%`)
        }

        // 🚀 早期退出优化：如果找到足够好的匹配，直接返回
        if (confidence > 0.9) {
          log.info(`[图像匹配] ✅ 早期退出 - 找到高置信度匹配: ${(confidence * 100).toFixed(2)}%`)
          return {
            found: true,
            confidence,
            x,
            y,
          }
        }
        
        // 🚀 早期退出优化：如果找到非常高的匹配（95%+），立即返回
        if (confidence > 0.95) {
          log.info(`[图像匹配] ✅ 早期退出 - 找到极高置信度: ${(confidence * 100).toFixed(2)}%`)
          return {
            found: false,
            confidence,
            x,
            y,
          }
        }
      }
    }

    log.info(`[图像匹配] 粗扫描完成: 共扫描 ${scanCount} 个位置, 最高相似度: ${(maxConfidence * 100).toFixed(2)}% at (${bestX},${bestY})`)
    
    // 🎯 精细扫描策略：只要粗扫描找到一定置信度就进行精细扫描
    // 如果最高置信度太低（< 45%），直接返回失败
    if (maxConfidence < 0.45) {
      log.info(`[图像匹配] ⏭️ 跳过精细扫描 - 最高置信度过低: ${(maxConfidence * 100).toFixed(2)}%`)
      log.info(`[图像匹配] 💡 可能原因: 1) 模板图不在监控区域内 2) 模板图与实际不符 3) 阈值设置过高`)
      return {
        found: false,
        confidence: maxConfidence,
        x: bestX,
        y: bestY,
      }
    }
    
    // 如果粗略扫描找到了可能的匹配（>45%），在附近进行精细扫描
    if (maxConfidence > 0.45) {
      log.info(`[图像匹配] 🔍 开始精细扫描 - 粗略最高: ${(maxConfidence * 100).toFixed(2)}%`)
      const refineResult = this.refineMatch(
        screenData,
        screenWidth,
        screenHeight,
        templateData,
        templateWidth,
        templateHeight,
        bestX,
        bestY,
        stepSize
      )
      
      if (refineResult.confidence > maxConfidence) {
        maxConfidence = refineResult.confidence
        bestX = refineResult.x!
        bestY = refineResult.y!
        log.info(`[图像匹配] ✨ 精细扫描提升置信度: ${(maxConfidence * 100).toFixed(2)}%`)
      }
    }

    return {
      found: false, // 在单尺度匹配中不判断 found，由外层判断
      confidence: maxConfidence,
      x: bestX,
      y: bestY,
    }
  }

  /**
   * 精细扫描：在粗略匹配位置附近进行像素级精确扫描
   */
  private refineMatch(
    screenData: Buffer,
    screenWidth: number,
    screenHeight: number,
    templateData: Buffer,
    templateWidth: number,
    templateHeight: number,
    centerX: number,
    centerY: number,
    searchRadius: number
  ): MatchResult {
    let maxConfidence = 0
    let bestX = centerX
    let bestY = centerY

    // 🚀 精细扫描范围扩大，确保不会错过最佳匹配
    const refinedRadius = Math.floor(searchRadius * 0.75) // 扩大搜索范围到75%步长
    const minX = Math.max(0, centerX - refinedRadius)
    const maxX = Math.min(screenWidth - templateWidth, centerX + refinedRadius)
    const minY = Math.max(0, centerY - refinedRadius)
    const maxY = Math.min(screenHeight - templateHeight, centerY + refinedRadius)

    // 🚀 精细扫描时逐像素扫描，不跳跃
    const refineStep = 1

    for (let y = minY; y <= maxY; y += refineStep) {
      for (let x = minX; x <= maxX; x += refineStep) {
        const confidence = this.calculateSimilarity(
          screenData,
          screenWidth,
          screenHeight,
          x,
          y,
          templateData,
          templateWidth,
          templateHeight,
          true  // 精细扫描，使用全像素比较
        )

        if (confidence > maxConfidence) {
          maxConfidence = confidence
          bestX = x
          bestY = y
        }
      }
    }

    return {
      found: true,
      confidence: maxConfidence,
      x: bestX,
      y: bestY,
    }
  }

  /**
   * 模板匹配相似度计算
   * 在屏幕截图(大图)的指定位置，计算与模板图片(小图)的相似度
   * 使用 pixelmatch 进行像素级比对
   */
  private calculateSimilarity(
    screenData: Buffer,
    screenWidth: number,
    screenHeight: number,
    offsetX: number,
    offsetY: number,
    templateData: Buffer,
    templateWidth: number,
    templateHeight: number,
    isRefineScan: boolean = false
  ): number {
    try {
      // 边界检查：确保模板不会超出屏幕范围
      if (offsetX + templateWidth > screenWidth || offsetY + templateHeight > screenHeight) {
        return 0
      }
      
      // 从屏幕截图中提取对应位置的区域数据
      const regionData = Buffer.alloc(templateWidth * templateHeight * 4)
      
      for (let y = 0; y < templateHeight; y++) {
        for (let x = 0; x < templateWidth; x++) {
          const screenIdx = ((offsetY + y) * screenWidth + (offsetX + x)) * 4
          const regionIdx = (y * templateWidth + x) * 4
          
          // 复制RGBA四个通道
          regionData[regionIdx] = screenData[screenIdx]
          regionData[regionIdx + 1] = screenData[screenIdx + 1]
          regionData[regionIdx + 2] = screenData[screenIdx + 2]
          regionData[regionIdx + 3] = screenData[screenIdx + 3]
        }
      }
      
      // 使用 pixelmatch 比较提取的区域和模板图片
      // threshold: 0.1 允许轻微的颜色差异（适应不同光线、压缩等）
      const diffOutput = Buffer.alloc(templateWidth * templateHeight * 4)
      const diffPixels = pixelmatch(
        templateData,
        regionData,
        diffOutput,
        templateWidth,
        templateHeight,
        { threshold: 0.1, includeAA: true }
      )
      
      // 计算相似度：匹配像素比例
      const totalPixels = templateWidth * templateHeight
      const matchedPixels = totalPixels - diffPixels
      const similarity = matchedPixels / totalPixels
      
      // 🐛 调试：打印所有相似度 > 50% 的位置（帮助诊断）
      if (similarity > 0.5) {
        log.info(`[像素匹配@(${offsetX},${offsetY})] ${matchedPixels}/${totalPixels} 像素匹配 = ${(similarity * 100).toFixed(2)}%`)
      }
      
      return similarity
    } catch (error) {
      log.error(`[模板匹配] 计算相似度失败:`, error)
      return 0
    }
  }
}

// 导出单例
export const imageMatchService = new ImageMatchService()

